Chapter 4 - Boolean Expressions and Conditions

This notebook uses code snippets and explanations from this course.

So far, we have learned how to use Python as a basic calculator and how to store information in variables. Now we will set the first steps to an actual useful program. A lot of programming has to do with executing code if a particular condition holds. This enables a program to act upon its inputs. For example: an app on your phone might give a warning if the battery level is lower than 5%. This means that the program needs to check if the variable battery_level is lower than the value of 5. We can do these checks using so called Boolean expressions. These Boolean expressions are the main element in probably one of the most used things in Python: if statements.

At the end of this topic, you will be able to:

  • work with and understand boolean expressions
  • work with and understand if statements
  • understand what indentation is
  • understand what nesting is

If you have questions about this chapter, please refer to the forum on Canvas.

1. Boolean expressions

A Boolean expression (or simply: boolean) is an expression that results in the type bool in Python. Possible values are either True or False. Boolean expressions are the building blocks of programming. Any expression that results in True or False can be considered a Boolean expression.

So far you've mainly seen:


In [ ]:
print(type('this is a string'))
print(type(101))
print(type(0.8))

Now we're introducing:


In [ ]:
print(type(False))
print(type(True))

1.1 Comparison operators

Here is a list of comparison operators used in Boolean expressions:

Operator Meaning True False
== equal 2 == 2 2 == 3
!= not equal 3 != 2 2 != 2
< less than 2 < 13 2 < 2
<= less than or equal to 2 <= 2 3 <= 2
> greater than 13 > 2 2 > 13
>= greather than or equal to 3 >= 2 2 >= 3

Remember that the single = is reserved for assignment! Boolean expressions look at variables but never change them.


In [ ]:
print(2 < 5)
print(2 <= 5)
print(3 > 7)
print(3 >= 7)
print(3 == 3)
print("school" == "school")
print("Python" != "SPSS")

The relevant 'logical operators' that we used here are: <, <=, >,>=,==,!=. In Python-speak, we say that such a logical expression gets 'evaluated' when you run the code. The outcome of such an evaluation is a 'binary value' or a so-called 'boolean' that can take only two possible values: True or False. You can assign such a boolean to a variable:


In [ ]:
greater = 5 > 2
print(greater, type(greater))
greater = 5 < 2
print(greater, type(greater))

Let's look at some examples. Try to guess the output based on the information about the operators in the table above. Hence, will the expression result in True or False in the following examples?


In [ ]:
print(5 == 5)

In [ ]:
print(5 == 4)

In [ ]:
print(10 < 20)

In [ ]:
print(10 < 8)

In [ ]:
print(10 < 10)

In [ ]:
print(10 <= 10)

In [ ]:
print(20 >= 21)

In [ ]:
print(20 == 20)

In [ ]:
print(1 == '1')

In [ ]:
print(1 != 2)

In [ ]:
boolean_expression = 5 == 4
print(boolean_expression)

1.2 Membership operators

Python also has so-called membership operators:

Operator function True False
in left object is a member of right object "c" in "cat" "f" in "cat"
not in left object is NOT a member of right object "f" in "cat" "c" in "cat"

We have already seen the operator in being used for checking whether a string (single or multiple characters) is a substring of another one:


In [ ]:
print("fun" in "function")

We can only use membership operators with iterables. The following will therefore not work, because an integer is not iterable:


In [ ]:
print(5 in 10)

However, we can use membership operators with other types of 'containers', such as lists. We will discuss lists in much more detail later on, but they represent ordered sequences of objects like strings, integers or a combination. We can use in and not in to check whether an object is a member of a list:


In [ ]:
letters = ['a','b','c','d']
numbers = [1,2,3,4,5]
mixed = [1,2,3,'a','b','c']

In [ ]:
print('a' in letters)

In [ ]:
print('g' not in letters)

In [ ]:
print('d' in mixed)

In [ ]:
print(1 in numbers)

In [ ]:
print(3 not in mixed)

In [ ]:
print('a' not in 'hello world')

1.3 And, or, and not

Finally, boolean operations are often performed using the boolean operators and, or and not. Given two boolean expressions, bool1 and bool2, this is how they work:

operation function True False
bool1 and bool2 True if both bool1 and bool2 are True, otherwise False (5 == 5 and 3 < 5) (5 == 5 and 3 > 5)
bool1 or bool2 True when at least one of the boolean expressions is True, otherwise False (5 == 5 and 3 > 5) (5 != 5 and 3 > 5)
not bool1 True if bool1 is False, otherwise False (not 5 != 5) (not 5 == 5)

Here are some examples of and:


In [ ]:
letters = ['a','b','c','d']
numbers = [1,2,3,4,5]

In [ ]:
print('a' in letters and 2 in numbers)

In [ ]:
print("z" in letters and 3 in numbers)

In [ ]:
print("f" in letters and 0 in numbers)

Here are some examples of or:


In [ ]:
letters = ['a','b','c','d']
numbers = [1,2,3,4,5]

In [ ]:
print('f' in letters or 2 in numbers)

In [ ]:
print('a' in letters or 2 in numbers)

In [ ]:
print('f' in letters or 10 in numbers)

Here are some example of not:


In [ ]:
a_string = "hello"
letters = ['a','b','c','d']
numbers = [1,2,3,4,5]

In [ ]:
print(not a_string.endswith("o"))

In [ ]:
print(not a_string.startswith("o"))

In [ ]:
print(not 'x' in letters)

In [ ]:
print(not 4 == 4)

In [ ]:
print(not (4 == 4 and "4" == 4))

Note that for some of these, there are alternative ways of writing them. For example, 'x not in y' and 'not x in y' are identical, and so are 'not x == y' and 'x != y'. For now, it does not really matter which one you use. If you want to read more about it, have a look here and here.


In [ ]:
print(not 'x' in letters)
print('x' not in letters)

In [ ]:
print(not 4 == 4)
print(4 != 4)

1.4 EXTRA: all() and any()

Take a look at the following example. Do you think it is clear?


In [ ]:
print("test" != "testing" and 1 == 1 and 2 == 2 or 20 in [1, 20, 3, 4,5])

Not really, right? Luckily, Python has another trick to deal with this type of examples: all and any. Given a list of boolean expressions, this is how they work:

operation function
all True if all boolean expressions are True, otherwise False
any True if at least one boolean expression is True, otherwise False

If you don't completely understand all() and any(), don't worry, you will not necessarily need them right now. They are just a nice alternative to make your code more readable and you may appreciate that in the future.

Here are some examples of all():


In [ ]:
letters = ['a','b','c','d']
numbers = [1,2,3,4,5]

In [ ]:
list_bools1 = ['a' in letters, 
                2 in numbers]
print(list_bools1)
boolean_expression1 = all(list_bools1)
print(boolean_expression1)

In [ ]:
list_bools2 = ['a' in letters, 
                20 in numbers]
print(list_bools2)
boolean_expression2 = all(list_bools2)
print(boolean_expression2)

Here are some examples of any():


In [ ]:
list_bools3 = ['f' in letters, 
                200 in numbers]
print(list_bools3)
boolean_expression3 = any(list_bools3)
print(boolean_expression3)

In [ ]:
list_bools4 = ['a' in letters, 
               20 in numbers,
               2 in numbers]
print(list_bools4)
boolean_expression4 =  any(list_bools4)
print(boolean_expression4)

2. Conditions: if statements

You might wonder why we took quite some time explaining boolean expresisons. One of the reasons is that they are the main element in probably one of the most used things in Python: if statements. The following picture explains what happens in an if statement in Python.

Let's look at an example (modify the value of number to understand what is happening here):


In [ ]:
number = 2 # try changing this value to 6
if number <= 5:
    print(number)

You can use as many if statements as you like:


In [ ]:
number = 5
if number == 5: 
    print("number equals 5")
if number > 4: 
    print("number is greater than 4")
if number >= 5:
    print("number is greater than or equals 5")
if number < 6: 
    print("number is less than 6") 
if number <= 5:
    print("number is less than or equals 5")
if number != 6 :
    print("number does not equal 6")

2.1 Two-way decisions

But what if we want to have options for two different scenarios? We could just use a bunch of if statements. However, Python has a more efficient way. Apart from if we also have the else statement for two-way decisions (modify the value of number to understand what is happening here):


In [ ]:
number = 10 # try changing this value to 2
if number <= 5:
    print(number)
else:
    print('number is higher than 5')

Now Python always runs one of the two pieces of code. It's like arriving at a fork in the road and choosing one path to follow.

2.2 Multi-way decisions

But of course we don't have to stop there. If you have multiple options, you can use the elif statement. For every if block, you can have one if statement, multiple elif statements and one else statement. So now we know the entire if-elif-else construct:


In [ ]:
age = 21
if age < 12:
    print("You're still a child!")
elif age < 18:
    print("You are a teenager!")
elif age < 30:
    print("You're pretty young!")
else:
    print("Wow, you're old!")

First the if statement will be evaluated. Only if that statement turns out to be False the computer will proceed to evaluate the elif statement. If the elif statements in turn would prove to be False, the machine will proceed and execute the lines of code associated with the else statement. You can think of this coding structure as a decision tree! Remember: if somewhere along the tree, your machine comes across a logical expression which is True, it won't bother anymore to evaluate the remaining options! Note that the statements are evaluated in order of occurence.

Can you identify the difference between the code above and the code below? (Try changing age)


In [ ]:
age = 21
if age < 12:
    print("You're still a child!")
if age < 18:
    print("You are a teenager!")
if age < 30:
    print("You're pretty young!")
else:
    print("Wow, you're old!")

Remember:

  • if-if: your code wil check all the if statements
  • if-elif: if one condition results to True, it will not check the other conditions

Unless you need to check all conditions, using if-elif is usually preferred because it's more efficient.

3. Indentation

Let's take another look at the example from above (we've added line numbers):

1. if number <= 5:
2.     print(number)
3. else:
4.     print('number is higher than 5')

You might have noticed that line 2 starts with 4 spaces. This is on purpose! The indentation lets Python know when it needs to execute the piece of code. When the boolean expression in line 1 is True, Python executes the code from the next line that starts four spaces or one tab (an indent) to the right. This is called indentation. All statements with the same distance to the right belong to the same 'block' of code.

Unlike other languages, Python does not make use of curly braces to mark the start and end of pieces of code, like if statements. The only delimiter is a colon (:) and the indentation of the code. Both four spaces and tabs can be used for indentation. This indentation must be used consistently throughout your code. The most popular way to indent is four spaces (see stackoverflow). For now, you do not have to worry about this, since a tab is automatically converted to four spaces in notebooks.

Take a look at the code below. We see that the indented block is not executed, but the unindented lines of code are. Now go ahead and change the value of the person variable. The conversation should be a bit longer now!


In [ ]:
person = "John"
print("hello!")
if person == "Alice":
    print("how are you today?")                  #this is indented
    print("do you want to join me for lunch?")   #this is indented
elif person == "Lisa":
    print("let's talk some other time!")         #this is indented
print("goodbye!")

3.1 Nesting

We have seen that all statements with the same distance to the right belong to the same block of code, i.e. the statements within a block line up vertically. The block ends at a line less indented or the end of the file. Blocks can contain blocks as welll; this way, we get a nested block structure. The block that has to be more deeply nested is simply indented further to the right:

There may be a situation when you want to check for another condition after a condition resolves to True. In such a situation, you can use the nested if construct. As you can see if you run the code below, the second if statement is only executed if the first if statement returns True. Try changing the value of x to see what the code does.


In [ ]:
x = float(input("Enter a number: "))
if x >= 0:
    if x == 0:
        print("Zero")
    else:
        print("Positive number")
else:
    print("Negative number")

Exercises

Exercise 1:

It's important to practice a lot with boolean expressions. Here is a list of them, which orginate from learnpythonthehardway. Try to guess the output.


In [ ]:
print(True and True)

In [ ]:
print(False and True)

In [ ]:
print(1 == 1 and 2 == 1)

In [ ]:
print("test" == "test")

In [ ]:
print(1 == 1 or 2 != 1)

In [ ]:
print(True and 1 == 1)

In [ ]:
print(False and 0 != 0)

In [ ]:
print(True or 1 == 1)

In [ ]:
print("test" == "testing")

In [ ]:
print(1 != 0 and 2 == 1)

In [ ]:
print("test" != "testing")

In [ ]:
print("test" == 1)

In [ ]:
print(not (True and False))

In [ ]:
print(not (1 == 1 and 0 != 1))

In [ ]:
print(not (10 == 1 or 1000 == 1000))

In [ ]:
print(not (1 != 10 or 3 == 4))

In [ ]:
print(not ("testing" == "testing" and "Zed" == "Cool Guy"))

In [ ]:
print(1 == 1 and (not ("testing" == 1 or 1 == 0)))

In [ ]:
print("chunky" == "bacon" and (not (3 == 4 or 3 == 3)))

In [ ]:
print(3 == 3 and (not ("testing" == "testing" or "Python" == "Fun")))

In [ ]:
print("test" != "testing" and 1 == 1 and 2 == 2 and 20 in [1, 20, 3, 4,5])

Exercise 2:

Write a small program that defines a variable weight. If the weight is > 50 pounds, print "There is a $25 charge for luggage that heavy." If it is not, print: "Thank you for your business." If the weight is exactly 50, print: "Pfiew! The weight is just right!". Change the value of weight a couple of times to check whether your code works. Make use of the logical operators and the if-elif-else construct!


In [ ]:
# insert your code here

Exercise 3:

What's wrong in the following code? Correct the mistake.


In [ ]:
my_string = "hello"
if my_string == "hello":
print("world")

Why is the last line in the following code red? Correct the mistake.


In [ ]:
my_string = "hello"
if my_string == "hello":
  print("world")

What's wrong in the following code? Correct the mistake.


In [ ]:
my_string = "hello"
if my_string == "hello"
    print("world")

What's wrong in the following code? Correct the mistake.


In [ ]:
my_string = "hello"
if my_string = "hello":
    print("world")

Exercise 4:

Can you rewrite the code below without nesting? Hint: use the if-elif-else construct.


In [ ]:
x = float(input("Enter a number: "))
if x >= 0:
    if x == 0:
        print("Zero")
    else:
        print("Positive number")
else:
    print("Negative number")

Exercise 5:

A friend wants your advice on how much oranges he should buy. Write a program that will give the advice to buy 24 oranges if the price is lower than 1.50 EUR per kg, 12 oranges if the price is between 1.50 EUR and 3 EUR, and only 1 orange if the price is higher than 3 EUR. But also tell him that he should only buy them if the oranges are fresh; otherwise, he should not get any. Use nesting and the if-elif-else construct.


In [ ]:
orange_quality = "fresh"
orange_price = 1.75
# your code here